package id.ac.itb.GenericSolver;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
 
/**
 * State for solving using Solver
 * @author Edward Samuel
 */
public abstract class AbstractState {
    private AbstractState parent;
    private List<AbstractState> children = new ArrayList<>();
    private List<AbstractStep> steps = new ArrayList<>();
    private boolean path;
    
    /**
     * Get parent of this state
     * @return Parent state
     */
    public AbstractState getParent() {
		return parent;
	}

    /**
     * Get this state is a path to solution
     * @return true, if this is a path to solution
     */
	public boolean isPath() {
		return path;
	}

	/**
	 * Set this state is a path to solution
	 * If the value of path is 
	 * 		true, set the parent path to true
	 * 		false, set sthe children path to false  
	 * @param path Path
	 */
	public void setPath(boolean path) {
		this.path = path;
		if (parent != null && path)
        {
			parent.setPath(path);
        } else if (!path) {
        	for (AbstractState child : children) {
				child.setPath(false);
			}
        }
	}

	/**
	 * Add a child state to this state
	 * @param step Step for create the child state
	 * @param state Child state
	 */
    public void addChild(AbstractStep step, AbstractState state)
    {
        state.parent = this;
        children.add(state);
        steps.add(step);
    }

    /**
     * Get the first solution steps
     * Required to solved by Solver
     * @return List of steps to make solution
     */
    public Collection<AbstractStep> getFirstSolutionSteps()
    {
        List<AbstractStep> list = new ArrayList<>();
        AbstractState current = this;

        while (current.isPath() && !current.children.isEmpty())
        {
            for (int i=0, count=current.children.size(); i<count; i++)
            {
                if (current.children.get(i).isPath())
                {
                    list.add(current.steps.get(i));
                    current = current.children.get(i);
                    break;
                }
            }
        }
        
        return list;
    }

    /**
     * Determine if this state is the solution
     * @return true, if this state if solution
     */
    public abstract boolean isSolution();

    /**
     * Get all possible steps from this state
     * @return List of all possible steps
     */
    public abstract Collection<AbstractStep> getAllPossibleSteps();

    /**
     * Create a new state based on this state that applied the step 
     * @param step
     * @return
     */
    public abstract AbstractState createAppliedState(AbstractStep step);

    /**
     * Clean up the solutions
     */
    public void destroySolution()
    {
        path = false;
        children.clear();
        steps.clear();
    }
}
